package design

import (
	"errors"
	"gitee.com/bobo-rs/idea-space-framework/pkg/core/models"
	"gitee.com/bobo-rs/idea-space-framework/pkg/utils"
	"math"
	"sort"
)

// MatchCutTemplates 匹配截取指定数量的模板列表
// templates 模板列表
// target 匹配目标数量
// page 页码
// size 切词数量
// Example:
// fmt.Println(NewDesign().MatchCutTemplates(templates, 10, 0, 5))
func (d *sDesign) MatchCutTemplates(templates []models.DesignTemplateIdAndNumItem, target, page, size int) (out *models.MatchDesignTemplateItem, err error) {
	total := len(templates)
	if total == 0 {
		return nil, errors.New(`缺少模板数据`)
	}

	// 获取切词分页数据
	offset, end, _ := d.Page(total, page, size)

	// 指定数值匹配进行排序
	if len(templates) > size {
		templates = d.MatchSortByTemplates(templates, target)[offset:end] // 排序后并截取指定数据
	}

	// 组装数据
	return &models.MatchDesignTemplateItem{
		MatchTid:  utils.NewArray(templates).ArrayColumnsUniqueUnit(`id`),
		MatchList: templates,
	}, nil
}

// MatchSortByTemplates 根据指定数值匹配重新排序模板列表
func (d *sDesign) MatchSortByTemplates(templates []models.DesignTemplateIdAndNumItem, target int) []models.DesignTemplateIdAndNumItem {
	// 按指定数值对比靠近排序
	sort.Slice(templates, d.ByClosenessTo(uint(target), templates))
	return templates
}

// BinaryFindTemplate 二分查找最靠近卖点数量的模板
func (d *sDesign) BinaryFindTemplate(templates []models.DesignTemplateIdAndNumItem, target uint) (template *models.DesignTemplateIdAndNumItem, index int) {
	var (
		minIdx, maxIdx = 0, len(templates) - 1 // 初始模板首尾索引
		mid            = 0                     // 最终靠近匹配数值的索引ID
	)
	// 模板数据为空
	if maxIdx < 0 {
		return nil, 0
	}

	// 初始最终靠近的初始模板信息和最后一个模板数据
	closestTemplate, endTemplate := templates[minIdx], templates[maxIdx]

	// 检查首尾模板数值是否已经靠近当前数值，最后模板小于等于靠近数值
	if endTemplate.Num <= target || maxIdx == 0 {
		return &endTemplate, maxIdx
	}

	// 初始的最终模板小于或等于数值
	if closestTemplate.Num == target {
		return &closestTemplate, minIdx
	}

	// 二分循环查找最终靠近模板数据
	for minIdx <= maxIdx {
		mid = minIdx + (maxIdx-minIdx)/2 // 二分查找，获取索引ID
		// 通过索引ID获取当前模板
		currTemplate := templates[mid]
		// 匹配数值减去当前模板数值小于匹配数值减去之前存储最靠近模板数值，重新赋值最靠近数值
		if utils.Abs(int(target-currTemplate.Num)) < utils.Abs(int(target-closestTemplate.Num)) {
			closestTemplate = currTemplate // 赋值最靠进初始模板
		}

		// 匹配到数值
		if currTemplate.Num == target {
			return &closestTemplate, mid
		}

		// 移动指针到更接近target的一侧 继续查找最近模板，当前模板小于匹配数值，则最小索引值加1，继续查找
		if currTemplate.Num < target {
			minIdx = mid + 1
			continue
		}

		// 当前模板大于匹配数值，则最大索引数值减1
		maxIdx = mid - 1
	}
	// 检查左边元素是否更接近
	if minIdx > 0 && utils.Abs(int(target-templates[minIdx-1].Num)) < utils.Abs(int(target-closestTemplate.Num)) {
		mid = minIdx - 1
		closestTemplate = templates[mid]
	}

	// 再检查右边元素是否更接近
	if maxIdx < len(templates)-1 && utils.Abs(int(target-templates[maxIdx+1].Num)) < utils.Abs(int(target-closestTemplate.Num)) {
		mid = maxIdx + 1
		closestTemplate = templates[mid]
	}

	// 没匹配到相同数值，返回最靠近数值的模板
	return &closestTemplate, mid
}

// ByClosenessTo 比较两个model.DesignTemplateIdAndNumItem的Num字段与target的接近程度
func (d *sDesign) ByClosenessTo(target uint, items []models.DesignTemplateIdAndNumItem) func(i, j int) bool {
	return func(i, j int) bool {
		// 计算最小数和最大数
		diffI, diffJ := utils.Abs(int(target-items[i].Num)), utils.Abs(int(target-items[j].Num))
		return diffI < diffJ
	}
}

// MatchTemplateWordsBySelling 执行匹配并返回结果
func (d *sDesign) MatchTemplateWordsBySelling(words, sellingList []models.DesignTemplateWordsItem) map[uint]models.DesignTemplateWordsItem {

	var (
		matchWords      = make(map[uint]models.DesignTemplateWordsItem) // 匹配结果
		matchSellingMap = make(map[int]models.DesignTemplateWordsItem)
	)

	// 文本排序
	sort.Slice(words, func(i, j int) bool {
		return words[i].Num < words[j].Num
	})

	// 把卖点转换为MAP
	for k, selling := range sellingList {
		matchSellingMap[k] = selling
	}

	// 遍历模板文本
	for _, item := range words {
		// 匹配完全的项或查找最接近的项
		if len(matchSellingMap) > 0 && item.Num > 0 {
			idx, closestValue := d.FindClosestMatch(item.Num, matchSellingMap)
			delete(matchSellingMap, idx) // 移除已匹配
			item.Value = closestValue    // 卖点赋值到文本
		}
		// 将匹配或最接近的项添加到结果中
		matchWords[item.WordsId] = item
	}
	return matchWords
}

// FindClosestMatch 查找最接近的匹配项
func (d *sDesign) FindClosestMatch(textNum uint, matchList map[int]models.DesignTemplateWordsItem) (int, string) {
	var (
		closestDiff  = int(^uint(0) >> 1) // 初始化差值为最大
		closestValue string
		idx          int
	)
	// 匹配最接近的数值
	for key, item := range matchList {
		// 匹配到相等则立即返回
		if textNum == item.Num {
			closestValue = item.Value
			idx = key
			break
		}

		// 匹配最接近的数值
		diff := utils.Abs(int(textNum - item.Num))
		if diff < closestDiff {
			closestDiff = diff
			closestValue = item.Value
			idx = key
		}
	}
	return idx, closestValue
}

// SplitSellingValues 拆分卖点内容项
func (d *sDesign) SplitSellingValues(values ...string) []models.DesignTemplateWordsItem {
	if len(values) == 0 {
		return nil
	}
	// 拆分卖点值
	var sellingList []models.DesignTemplateWordsItem
	for _, value := range values {
		sellingList = append(sellingList, models.DesignTemplateWordsItem{
			Value: value,
			Num:   uint(utils.CountRunes(value)),
		})
	}
	return sellingList
}

// Page 模板切割页码
func (d *sDesign) Page(total, page, size int) (offset, end, totalPage int) {
	totalPage = int(math.Ceil(float64(total) / float64(size))) // 总页码
	if totalPage <= page {
		// 起始位置
		offset = (totalPage - 1) * size
	} else if page <= 1 {
		// 页码1或者0
		offset = 0
	} else {
		// 计算分割起始位置
		offset = (page - 1) * size
	}
	// 计算结束索引位置
	end = offset + size
	if end > total {
		end = total // 避免索引越界
	}
	return offset, end, totalPage
}
